from RE_init import *
from utility_functions import *
from pycorenlp import StanfordCoreNLP
from collections import Counter
import collections
import ast
import json
from networkx.readwrite.json_graph import node_link_data
from collections import OrderedDict
import unicodedata
from tqdm import tqdm
import random

def word_to_node_id(verbs_list, verb_ind, annotation):
    """
    Convert a word to its node ID in the dependency graph.
    
    Args:
        verbs_list (list): List of verbs
        verb_ind (int): Index of the verb in verbs_list
        annotation (dict): Annotation dictionary
        
    Returns:
        str: Node ID in the format "word-POS-index"
    """
    word = verbs_list[verb_ind]
    if word == "ROOT":
        return "ROOT-NNP-0"
    verb_occurance_num = verbs_list[0:verb_ind+1].count(word)
    i = -1
    for j in xrange(verb_occurance_num):
        i = annotation['words'].index(word, i+1)
    w_ind = i 
    return word+"-"+annotation['pos'][w_ind][1]+"-"+str(w_ind+1) 

def word_id_get_index(word_id):
    """
    Get the index part from a word ID.
    
    Args:
        word_id (str): Word ID in the format "word-POS-index"
        
    Returns:
        int: Index part of the word ID
    """
    return int(word_id.split('-')[2])

def word_id_get_pos(word_id):
    """
    Get the POS tag part from a word ID.
    
    Args:
        word_id (str): Word ID in the format "word-POS-index"
        
    Returns:
        str: POS tag part of the word ID
    """
    return word_id.split('-')[1]

def word_id_get_word(word_id):
    """
    Get the word part from a word ID.
    
    Args:
        word_id (str): Word ID in the format "word-POS-index"
        
    Returns:
        str: Word part of the word ID
    """
    return word_id.split('-')[0]

def sort_word_ids(word_ids, head_word_ind):
    """
    Sort word IDs and mark the head word with curly braces.
    
    Args:
        word_ids (list): List of word IDs
        head_word_ind (int): Index of the head word
        
    Returns:
        tuple: (sorted word string with head word in braces, sorted word IDs string)
    """
    word_sorted_str = ""
    word_with_pos_sorted_str = ""
    
    word_ids_tuple = map(lambda x: (word_id_get_index(x),x), word_ids)
    word_ids_tuple.sort(key = lambda x: x[0]) # Sort based on the first element - which is word's index
    for w in word_ids_tuple:
        if int(w[0]) == int(head_word_ind):
            word_sorted_str += "{" + word_id_get_word(w[1]) + "}" + " "
            word_with_pos_sorted_str += w[1] + " "            
        else:
            word_sorted_str += word_id_get_word(w[1]) + " "
            word_with_pos_sorted_str += w[1] + " "
    
    # Remove the extra space at the end
    return word_sorted_str.strip(), word_with_pos_sorted_str.strip()

def isPronoun(word):
    """
    Check if a word is a pronoun.
    
    Args:
        word (str): Word to check
        
    Returns:
        bool: True if the word is a pronoun, False otherwise
    """
    tagged,tag = nltk.pos_tag([word])[0]
    if "PRP" in tag:
        return True
    return False

def get_verbs(annotation):
    """
    Get all verbs from the annotation.
    
    Args:
        annotation (dict): Annotation dictionary
        
    Returns:
        list: List of verbs
    """
    verbs_list = [x[0] for x in annotation["pos"] if "VB" in x[1]]
    return verbs_list

def get_preps(annotation):
    """
    Get all prepositions from the annotation.
    
    Args:
        annotation (dict): Annotation dictionary
        
    Returns:
        list: List of prepositions
    """
    prep_list = [x[0] for x in annotation['chunk'] if "PP" in x[1]]
    return prep_list

def get_nouns(annotation):
    """
    Get all nouns from the annotation.
    
    Args:
        annotation (dict): Annotation dictionary
        
    Returns:
        list: List of nouns
    """
    return [x for x in re.findall(r'\(NN\w* (\w+)\)', annotation['syntax_tree'])]

def get_adjectives(annotation):
    """
    Get all adjectives from the annotation.
    
    Args:
        annotation (dict): Annotation dictionary
        
    Returns:
        list: List of adjectives
    """
    return [x for x in re.findall(r'\(JJ (\w+)\)', annotation['syntax_tree'])]

def get_expansion_list(node_id, node_type="arg", rel_type="SVO"):
    """
    Get the list of dependency relations used for expanding a node.
    
    Args:
        node_id (str): Node ID
        node_type (str): Type of node ("arg" or "rel")
        rel_type (str): Type of relation
        
    Returns:
        list: List of dependency relations for expansion
    """
    arg_expand_list = ['nn', 'amod', 'det', 'neg', 'prep_of', 'num', 'quantmod', 'poss'] 
    arg_expand_non_nnp_list = ['infmod', 'partmod', 'ref', 'prepc_of', "dobj", "iobj"] # 'rcmod' -> it's always connected to nsubj # dobj/iobj for prepc_ cases
    rel_expand_list = ['advmod', 'mod', 'aux', 'auxpass', 'cop', 'prt','neg', 'poss']
    rel_expand_non_SVO_list = ['dobj', 'iobj']

    if node_type == "arg":
        if word_id_get_pos(node_id) != "NNP":
            return arg_expand_list + arg_expand_non_nnp_list
        else:
            return arg_expand_list

    if node_type == "rel":
        if rel_type != "SVO":
            return rel_expand_list + rel_expand_non_SVO_list
        else:
            return rel_expand_list
            
    return []

def expand_node_id(node_id, g_dir, node_type="arg", rel_type="SVO", EXTRACT_NESTED_PREPOSITIONS_RELS=True):
    """
    Expand a node by adding related words according to the dependency graph.
    
    Args:
        node_id (str): Node ID to expand
        g_dir (networkx.DiGraph): Directed dependency graph
        node_type (str): Type of node ("arg" or "rel")
        rel_type (str): Type of relation
        EXTRACT_NESTED_PREPOSITIONS_RELS (bool): Whether to extract nested preposition relations
        
    Returns:
        dict: Dictionary containing the expanded node information
    """
    res_expanded = defaultdict(list)
    expansion_list = get_expansion_list(node_id, node_type=node_type, rel_type=rel_type)
    g_dir_v = None
    node_extra_ids = []
    node_extra_ids.append(node_id)
    node_head_id_ind = word_id_get_index(node_id)
    try:
        g_dir_v = g_dir[node_id]
    except:
        # Failed to get adjacency network while expanding
        return
    for word_id, e in g_dir_v.iteritems():
        if e["rel"] in expansion_list:
            if e["rel"] == "prep_of" or e["rel"] == "prepc_of":
                new_word_id = "of " + word_id.split("-")[0] + "-" + word_id.split("-")[1] + "-" + word_id.split("-")[2]
                node_extra_ids.append(new_word_id)
            else:
                node_extra_ids.append(word_id)
            
    node_final_word_str, node_final_with_pos_str = sort_word_ids(node_extra_ids, node_head_id_ind)
    
    res_expanded['rel_prepositions'] = ""
    if EXTRACT_NESTED_PREPOSITIONS_RELS:
        res_expanded['rel_prepositions'] = get_nested_preposition_rels(g_dir, node_id, expansion_list)
    
    res_expanded["final_word_str"] = node_final_word_str
    res_expanded["final_word_with_pos_str"] = node_final_with_pos_str
    
    return res_expanded
    
def expand_rel(rel, g_dir, EXTRACT_NESTED_PREPOSITIONS_RELS, ADD_EXTRAS=False):
    """
    Expand a relation by adding extra related words.
    
    Args:
        rel (dict): Relation to expand
        g_dir (networkx.DiGraph): Directed dependency graph
        EXTRACT_NESTED_PREPOSITIONS_RELS (bool): Whether to extract nested preposition relations
        ADD_EXTRAS (bool): Whether to add extra elements through DFS
        
    Returns:
        dict: Expanded relation
    """
    def dfs_rel(word_id, extras, after, ADD_EXTRAS1):
        if ADD_EXTRAS1:
            if word_id in g_dir:
                for item in g_dir[word_id]:
                    if item not in extras:
                        if after:
                            extras.append(item)
                            dfs_rel(item, extras, after, ADD_EXTRAS1)
                        else:
                            dfs_rel(item, extras, after, ADD_EXTRAS1)
                            extras.append(item)
                            
    arg1 = rel['arg1']
    arg2 = rel['arg2']
    r = rel['rel']
    verb_with_id = r[:]
    arg_expand_list = ['nn', 'amod', 'det', 'neg', 'prep_of', 'num', 'quantmod', 'poss']
    arg_expand_non_nnp_list = ['infmod', 'partmod', 'ref', 'prepc_of', "dobj", "iobj"] # 'rcmod' -> it's always connected to nsubj
    rel_expand_list = ['advmod', 'mod', 'aux', 'auxpass', 'cop', 'prt', 'neg', 'poss']
    rel_expand_non_SVO_list = ['dobj', 'iobj'] 
    
    if rel["type"] == "SVCop":
        arg_expand_list = arg_expand_list + ['acomp', 'advmod', 'xcomp', 'acl', 'ccomp', 'aux']
        rel_expand_non_SVO_list = rel_expand_non_SVO_list + ['aux']
    
    arguments_names = ['arg1', 'arg2']
    
    # EXPAND ARGUMENTS
    arg_extra_ids = defaultdict(list)
    arg_head_ind = defaultdict(list)
    for ind, arg_name in enumerate(arguments_names):            
        arg = rel[arg_name]
        if arg == "": # For SV relationships
            continue
        # Add current argument to the extended version
        arg_extra_ids[arg_name].append(arg)
        if arg_name == 'arg2':
            dfs_rel(arg, arg_extra_ids[arg_name], True, ADD_EXTRAS)
        arg_head_ind[arg_name] = word_id_get_index(arg)
        v_arg_id = arg
        # If it is not a proper noun -> expand more to cover rcmod, infmod, and so on        
        if word_id_get_pos(v_arg_id) != "NNP": 
            arg_expand_list_final = arg_expand_list + arg_expand_non_nnp_list
        else:
            arg_expand_list_final = arg_expand_list

        g_dir_v = None
        try:
            g_dir_v = g_dir[v_arg_id]
        except:
            # Failed to get adjacency network while expanding
            continue
        for word_id, e in g_dir_v.iteritems():
            if e["rel"] in arg_expand_list_final:
                if e["rel"] == "prep_of" or e["rel"] == "prepc_of":
                    new_word_id = "of " + word_id.split("-")[0] + "-" + word_id.split("-")[1] + "-" + word_id.split("-")[2]
                    arg_extra_ids[arg_name].append(new_word_id)
                    if arg_name == 'arg2':
                        dfs_rel(word_id, arg_extra_ids[arg_name], True, ADD_EXTRAS)
                else:     
                    if word_id not in arg_extra_ids[arg_name]:
                        arg_extra_ids[arg_name].append(word_id)
                        if arg_name == 'arg2':
                            dfs_rel(word_id, arg_extra_ids[arg_name], True, ADD_EXTRAS)
    
    # EXPAND THE RELATIONSHIP
    rel_extra_ids = []
    v_rel_id = r
    rel_extra_ids.append(v_rel_id)
    rel_head_ind = word_id_get_index(v_rel_id)
    if "SVO" not in rel["type"]:
        rel_expand_list_final = rel_expand_list + rel_expand_non_SVO_list
    else:
        rel_expand_list_final = rel_expand_list
    g_dir_v = None
    try:
        g_dir_v = g_dir[v_rel_id]
    except:
        pass
        # Failed to get adjacency network while expanding
    if g_dir_v is not None:
        for word_id, e in g_dir_v.iteritems():
            if e["rel"] in rel_expand_list_final:
                rel_extra_ids.append(word_id)
    
    arg1_final_word_str, arg1_final_with_pos_str = sort_word_ids(arg_extra_ids[arguments_names[0]], arg_head_ind[arguments_names[0]])
    arg2_final_word_str, arg2_final_with_pos_str = sort_word_ids(arg_extra_ids[arguments_names[1]], arg_head_ind[arguments_names[1]])
    rel_final_word_str, rel_final_with_pos_str = sort_word_ids(rel_extra_ids, rel_head_ind)
    
    rel_expanded = rel
    rel_expanded['arg1'] = arg1_final_word_str
    rel_expanded['arg2'] = arg2_final_word_str
    rel_expanded['rel'] = rel_final_word_str
    
    rel_expanded['arg1_with_pos'] = arg1_final_with_pos_str
    rel_expanded['arg2_with_pos'] = arg2_final_with_pos_str
    rel_expanded['rel_with_pos'] = rel_final_with_pos_str
    rel_expanded['rel_with_id'] = verb_with_id

    if EXTRACT_NESTED_PREPOSITIONS_RELS:
        expansion_list = arg_expand_list # Is this correct? How about get_expansion_list?
        rel_expanded['rel_prepositions'] = get_nested_preposition_rels(g_dir, r, expansion_list)
        rel_expanded['arg1_prepositions'] = get_nested_preposition_rels(g_dir, arg1, expansion_list)
        rel_expanded['arg2_prepositions'] = get_nested_preposition_rels(g_dir, arg2, expansion_list)
    
    return rel_expanded


def get_nested_preposition_rels(g_dir, main_word_id, expansion_list):
    """
    Extract nested preposition relations from a word.
    
    Args:
        g_dir (networkx.DiGraph): Directed dependency graph
        main_word_id (str): Main word ID
        expansion_list (list): List of dependency relations for expansion
        
    Returns:
        str: String representation of nested preposition relations
    """
    if main_word_id == "":
        return ""
    nested_rels = []
    g_dir_v = None
    try:
        g_dir_v = g_dir[main_word_id]
    except:
        # Failed to get adjacency network while expanding
        pass
    if g_dir_v is not None:                
        # Extract the head words of nested relations (the ones connected with prepositions)
        for word_id, e in g_dir_v.iteritems():
            single_nested_rel = defaultdict(list)
            if "prep_" in e["rel"] or "prepc_" in e["rel"]:
                if e["rel"] == "prep_of" or e["rel"] == "prepc_of": # Skip the preposition of
                    continue
                single_nested_rel_ids = []
                single_nested_rel_ids.append(word_id)
                # Reason: type of preposition
                single_nested_rel["reason"] = e["rel"]
                nested_rel_head_id = word_id
                nested_rel_head_ind = word_id_get_index(word_id)
                
                g_dir_v_nested = None
                try:
                    g_dir_v_nested = g_dir[nested_rel_head_id]
                except:
                    continue
                    # Failed to get adjacency network while extracting nested relations
                
                for word_id_nested, e_nested in g_dir_v_nested.iteritems():
                    if e_nested["rel"] in expansion_list:
                        single_nested_rel_ids.append(word_id_nested)
                nested_rel_final_word_str, nested_rel_final_with_pos_str = sort_word_ids(single_nested_rel_ids, nested_rel_head_ind)
                single_nested_rel["text"] = nested_rel_final_word_str
                nested_rels.append(single_nested_rel)          
           
    nested_rels_str = ""
    for item in nested_rels:
        nested_rels_str += "REASON: " + item["reason"] + " TEXT: " + item["text"] + " -- "
    nested_rels_str.strip()
    
    return nested_rels_str
               
                
def create_node_attributes(n, annotation):
    """
    Create attributes for a node in the dependency graph.
    
    Args:
        n (str): Node ID
        annotation (dict): Annotation dictionary
        
    Returns:
        dict: Node attributes
    """
    if n is None:
        return None
    # ROOT does not appear in the tree
    n_att = {}
    if n == "ROOT-NNP-0":
        n_word = "ROOT"
        n_att["word"] = n_word
        n_att["id"] = "ROOT-NNP-0"
        return n_att
    try:
        # Extract attributes
        n_word, n_pos, n_ind = n.split('-')[0], n.split('-')[1], n.split('-')[2]        
        n_ind = int(n_ind) - 1 # Make it 0 base - ROOT becomes "-1"
    except:
        # Error in tokenizer
        return None
    
    n_att["word"] = n_word
    n_att["ind"] = n_ind
    n_att["pos"] = n_pos
    n_att["id"] = n
    
    return n_att

def dp_str_to_node_id(w_ind_str, pos):
    """
    Convert a dependency parse string to a node ID.
    
    Args:
        w_ind_str (str): Dependency parse string in the format "word-index"
        pos (list): List of POS tags
        
    Returns:
        str: Node ID in the format "word-POS-index"
    """
    if w_ind_str == "ROOT-0":
        return "ROOT-NNP-0"
    word = w_ind_str.split('-')[0]
    try:
        word_ind = int(w_ind_str.split('-')[1])-1
        res = word+"-" + pos[word_ind][1] + "-" + str(word_ind+1)
    except:
        # Error in tokenizer
        return
    return res


# Added by Shadi
# How to find an expression?
def createMappDictionary1(annotation, t_orig):
    """
    Create a mapping dictionary from words to their indices in the text.
    
    Args:
        annotation (dict): Annotation dictionary
        t_orig (str): Original text
        
    Returns:
        tuple: (Dictionary mapping words to indices, Dictionary mapping indices to node IDs)
    """
    d = defaultdict(set)
    idis = {}
    dep_parse = annotation['dep_parse']
    if dep_parse == '':
        return None
    dp_list = dep_parse.split('\n')
    pattern = re.compile(r'.+?\((.+?), (.+?)\)')
    for dep in dp_list:
        m = pattern.search(dep)
        n1 = dp_str_to_node_id(m.group(1), annotation['pos'])
        n2 = dp_str_to_node_id(m.group(2), annotation['pos'])
        n1_att = create_node_attributes(n1, annotation)
        n2_att = create_node_attributes(n2, annotation)
        if n1_att is not None:
            if n1_att['word'] != 'ROOT':
                d[n1_att['word']].add(int(n1_att['id'].split('-')[2]))
                idis[int(n1_att['id'].split('-')[2])] = n1_att['id']
        if n2_att is not None:
            if n2_att['word'] != 'ROOT':
                d[n2_att['word']].add(int(n2_att['id'].split('-')[2]))
                idis[int(n2_att['id'].split('-')[2])] = n2_att['id']
    if t_orig:
        wordlist = nltk.word_tokenize(t_orig)
        j = 1
        while j-1 < len(wordlist):
            d[wordlist[j-1]].add(j)
            j += 1
    return d, idis


def findOutcomes(f, words, res=[]):
    """
    Find all possible paths through a list of word sets.
    
    Args:
        f (list): List of sets of word indices
        words (list): List of words
        res (list): List of paths found so far
        
    Returns:
        list: List of all possible paths
    """
    if not f:
        return res
    if not res:
        for node in f[0]:
            res.append([node])
        return findOutcomes(f[1:], words, res)
    new = []
    for path in res:
        for node in f[0]:
            if int(node) > int(path[-1]):
                path.append(node)
                new.append(path[:])
                path.pop()
    return findOutcomes(f[1:], words, new)

def findIDs(d, idis, expression):
    """
    Find the node IDs for words in an expression.
    
    Args:
        d (dict): Dictionary mapping words to indices
        idis (dict): Dictionary mapping indices to node IDs
        expression (str): Expression to find
        
    Returns:
        list: List of node IDs for words in the expression
    """
    f = []
    wordlist = nltk.word_tokenize(expression)
    words = []
    for word in wordlist:
        if word in d:
            words.append(word)
            f.append(list(d[word]))
    outs = findOutcomes(f, words, [])
    res = []
    for path in outs:
        new = []
        for i in range(len(path)):
            if path[i] in idis:
                new.append(idis[path[i]])
            else:
                new.append('')
        res.append(new)
    return res

def findWords(annotation, expression, t_orig):
    """
    Find words in an expression in the original text.
    
    Args:
        annotation (dict): Annotation dictionary
        expression (str): Expression to find
        t_orig (str): Original text
        
    Returns:
        list: List of node IDs for words in the expression
    """
    d, idis = createMappDictionary1(annotation, t_orig)
    return findIDs(d, idis, expression)

def findHeader(WordIds, g_dir): 
    """
    Find the head word in a list of word IDs.
    
    Args:
        WordIds (list): List of word IDs
        g_dir (networkx.DiGraph): Directed dependency graph
        
    Returns:
        str: Head word ID
    """
    finalw = []
    if len(WordIds) == 1:
        return WordIds[0]
    for ent in WordIds:
        if ent != '':
            finalw.append(ent)
    lens = []
    if len(finalw) == 1:
        return finalw[0]
    blocked_ind = []
    for i in range(len(finalw)):
        if str(finalw[i]).find('NN') > -1 or str(finalw[i]).find('PRP') > -1:
            lens.append(nx.shortest_path_length(g_dir, source="ROOT-NNP-0", target=finalw[i]))
        else:
            lens.append(-1)
            blocked_ind.append(i)
    m_val = max(lens)
    for j in blocked_ind:
        lens[j] = m_val+1
    if len(lens) == 0:
        return WordIds[0]
    ind = lens.index(min(lens))
    
    min_val = min(lens)
    for i in range(len(lens)):
        if lens[i] == min_val:
            if str(finalw[i]).find('NN') > -1 or str(finalw[i]).find('PRP') > -1:
                return finalw[i]
    
    return finalw[ind]

def create_dep_graph(annotation):
    """
    Create a dependency graph from an annotation.
    
    Args:
        annotation (dict): Annotation dictionary
        
    Returns:
        networkx.DiGraph: Directed dependency graph
    """
    dep_parse = annotation['dep_parse']
    if dep_parse == '':
        return None
    dp_list = dep_parse.split('\n')
    pattern = re.compile(r'.+?\((.+?), (.+?)\)')    
    g_dir = nx.DiGraph()
    for dep in dp_list:
        m = pattern.search(dep)
        n1 = dp_str_to_node_id(m.group(1), annotation['pos'])
        n2 = dp_str_to_node_id(m.group(2), annotation['pos'])
        n1_att = create_node_attributes(n1, annotation)
        n2_att = create_node_attributes(n2, annotation)
        if n1_att is None or n2_att is None:
            return None
        
        g_dir.add_node(n1, **n1_att)
        g_dir.add_node(n2, **n2_att)
        e_rel = dep[:dep.find("(")]
        g_dir.add_edge(n1, n2, rel=e_rel, label=e_rel)
    return g_dir

def get_simp_rel(rel, option="SVO", dataset='[DEFAULT_DATASET]'):
    """
    Get a simplified version of a relation.
    
    Args:
        rel (dict): Relation to simplify
        option (str): Type of relation
        dataset (str): Dataset name
        
    Returns:
        dict: Simplified relation
    """
    # Lower case, Strip
    arg1 = word_id_get_word(rel['arg1']).lower().strip()
    arg2 = word_id_get_word(rel['arg2']).lower().strip()
    r = word_id_get_word(rel['rel']).lower().strip()

    # Mapping for specific datasets
    if dataset == "[DATASET_1]":
        parent_list = ["i", "we", "us"]
        if arg1 in parent_list:
            arg1 = "[GENERIC_PARENT]"
        if arg2 in parent_list:
            arg2 = "[GENERIC_PARENT]"

        child_list = ["child", "children", "kid", "kids", "son", "sons", "daughter", "daughters", "toddler", "toddlers", "boy"]
        if arg1 in child_list:
            arg1 = "[GENERIC_CHILD]"
        if arg2 in child_list:
            arg2 = "[GENERIC_CHILD]"    
    
    # Stemming
    stemmer = SnowballStemmer("english")
    arg1 = stemmer.stem(arg1) 
    arg2 = stemmer.stem(arg2)
    r = stemmer.stem(r)
    
    rel_simp = rel.copy()
    rel_simp['arg1'] = arg1
    rel_simp['arg2'] = arg2
    rel_simp['rel'] = r
    return rel_simp

def get_conj_and_rels(rel, rel_expanded, g_dir, annotation):
    """
    Get conjunctive relations from a primary relation.
    
    Args:
        rel (dict): Primary relation
        rel_expanded (dict): Expanded primary relation
        g_dir (networkx.DiGraph): Directed dependency graph
        annotation (dict): Annotation dictionary
        
    Returns:
        tuple: (List of conjunctive relations, List of simplified conjunctive relations)
    """
    rel_updated = defaultdict(list)
    rel_updated = rel.copy()
    list_conj_and_rels = []
    list_conj_and_rels_simp = []
    field_names = ["arg1", "arg2", "rel"]
    arg_expansion_list = get_expansion_list(rel["arg1"], "arg", "SVO")
    for field in field_names:
        main_word_id = rel[field]  
        g_dir_v = None
        try:
            g_dir_v = g_dir[main_word_id]
        except:
            # Failed to get adjacency network while expanding
            pass
        if g_dir_v is not None:
            for word_id, e in g_dir_v.iteritems():
                if "conj_and" in e["rel"]:
                    has_own_obj = False
                    has_own_subj = False
                    obj_id = -1
                    single_nested_rel = defaultdict(list)
                    single_nested_rel = rel_expanded.copy()
                    single_nested_rel_ids = []
                    single_nested_rel_ids.append(word_id)
                    # Reason: type of preposition
                    single_nested_rel["type"] = rel_expanded["type"] + "_conj_and_" + field
                    nested_rel_head_id = word_id
                    nested_rel_head_ind = word_id_get_index(word_id)

                    g_dir_v_nested = None
                    res_expanded_conj_and_obj = []
                    rel_updated[field] = nested_rel_head_id
                    try:
                        g_dir_v_nested = g_dir[nested_rel_head_id]
                    except:
                        pass
                        # Failed to get adjacency network while extracting nested relations
                    if "arg" in field:
                        expansion_list = get_expansion_list(nested_rel_head_id, "arg", "SVO")
                    if "rel" == field:
                        t_verbs = get_verbs(annotation)
                        v_ids = []
                        for v_ind_tmp, v in enumerate(t_verbs):
                            v_id = word_to_node_id(t_verbs, v_ind_tmp, annotation)
                            v_ids.append(v_id)
                        if nested_rel_head_id not in v_ids:
                            continue
                        expansion_list = get_expansion_list(nested_rel_head_id, "rel", "SVO")
                    for word_id_nested, e_nested in g_dir_v_nested.iteritems():
                        if "rel" == field:
                            if e_nested["rel"] == "nsubj" or e_nested["rel"] == "xsubj" or e_nested["rel"] == "nsubjpass" or e_nested["rel"] == "xsubjpass":
                                has_own_subj = True
                            if e_nested["rel"] == "dobj":
                                has_own_obj = True
                                obj_id = word_id_nested 
                                rel_updated["arg2"] = word_id_nested
                        if e_nested["rel"] in expansion_list:
                            single_nested_rel_ids.append(word_id_nested)
                    nested_rel_final_word_str, nested_rel_final_with_pos_str = sort_word_ids(single_nested_rel_ids, nested_rel_head_ind)
                    
                    single_nested_rel[field] = nested_rel_final_word_str
                    
                    if has_own_obj and has_own_subj:
                        continue
                    
                    if has_own_obj:
                        res_expanded_conj_and_obj = expand_node_id(obj_id, g_dir, node_type="arg",
                                                                  rel_type="SVO", EXTRACT_NESTED_PREPOSITIONS_RELS=False)

                        single_nested_rel["type"] += "_dobj"
                        single_nested_rel["arg2"] = res_expanded_conj_and_obj["final_word_str"]
                        single_nested_rel["arg2_with_pos"] = res_expanded_conj_and_obj["final_word_with_pos_str"] 
                        single_nested_rel["rel_prepositions"] = res_expanded_conj_and_obj["rel_prepositions"]
                        expansion_list = get_expansion_list(obj_id, "arg", "SVO")
                        single_nested_rel["arg2_prepositions"] = get_nested_preposition_rels(g_dir, 
                                                                                           obj_id, 
                                                                                           expansion_list)
                        
                    single_nested_rel['rel_prepositions'] = get_nested_preposition_rels(g_dir, rel_updated["rel"], arg_expansion_list)
                    single_nested_rel['arg1_prepositions'] = get_nested_preposition_rels(g_dir, rel_updated["arg1"], arg_expansion_list)
                    single_nested_rel['arg2_prepositions'] = get_nested_preposition_rels(g_dir, rel_updated["arg2"], arg_expansion_list)
                    
                    list_conj_and_rels.append(single_nested_rel)
                    
                    single_nested_rel_simp = get_simp_rel(single_nested_rel.copy(), option="SVO")
                    list_conj_and_rels_simp.append(single_nested_rel_simp)
                    # Get SVP relations
                    list_svop_rels, list_svop_rels_simp = get_svop_rels(single_nested_rel.copy())
                    list_conj_and_rels += list_svop_rels
                    list_conj_and_rels_simp += list_svop_rels_simp